//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Eas.WebCrawler.PlugIns.UrlProcessedHandlers;
using Eas.WebCrawler.Interfaces;
namespace Eas.WebCrawler.PlugIns.UrlProcessedHandlers {
    /// <summary>Handles <see cref="WebCrawlerEvents.UrlProcessed"/> events by looking for all links in the body of the response and saving them out to a file.</summary>
    public class LinkListCreatingUrlProcessor : UrlProcessedHandler{
        private static object _Lock = new object();
        /// <summary>Gets urls from a page to publish to the list.</summary>
        /// <remarks>Backing field for the Extractor property.</remarks>
        /// <seealso cref="Extractor"/>
        private LinkExtractor _Extractor;

        /// <summary>Specifies which files in a visited page are published to the list.</summary>
        /// <remarks>Backing field for the _Selector property.</remarks>
        /// <seealso cref="Selector"/>
        private LinkSelector _Selector;
        
        /// <summary>The name of the file to which Urls will be written.</summary>
        /// <remarks>Backing field for the FileName property.</remarks>
        /// <seealso cref="FileName"/>
        private string _FileName;

        /// <summary>The name of the file to which Urls will be written.</summary>
        public string FileName {
            get { return _FileName; }
        }

        /// <summary>The Disctionary of already listed uris</summary>
        /// <remarks>Backing field for the AlreadyListedUris property.</remarks>
        /// <seealso cref="AlreadyListedUris"/>
        private Dictionary<Uri, object> _AlreadyListedUris;

        /// <summary>The Disctionary of already listed uris</summary>
        public Dictionary<Uri, object> AlreadyListedUris {
            get { return _AlreadyListedUris; }
        }

        /// <summary>Specifies which files in a visited page are published to the list.</summary>
        public LinkSelector Selector {
            get { return _Selector; }
        }

        /// <summary>Gets urls from a page to publish to the list.</summary>
        public LinkExtractor Extractor {
            get { return _Extractor; }
        }

        /// <summary>Constructs a <see cref="LinkListCreatingUrlProcessor"/> from a link extractor, a link selector, and the file name of the file to save the links out to.</summary>
        /// <param name="extractor">The extractor used to get links from a response.</param>
        /// <param name="selector">The selector to use to determine which links should be saved.</param>
        /// <param name="fileName">The name of the file to save the links out to.</param>
        public LinkListCreatingUrlProcessor(LinkExtractor extractor, LinkSelector selector, string fileName) : this(extractor, selector, fileName, null){ }

        /// <summary>Constructs a <see cref="LinkListCreatingUrlProcessor"/> from a link extractor, a link selector, a list of urls not to write out and the file name of the file to save the links out to.</summary>
        /// <param name="extractor">The extractor used to get links from a response.</param>
        /// <param name="selector">The selector to use to determine which links should be saved.</param>
        /// <param name="fileName">The name of the file to save the links out to.</param>
        /// <param name="alreadyVisitedUris">A set (in this case a dictionary whose values are set to null) of uris which should be ignored if encountered</param>
        public LinkListCreatingUrlProcessor(LinkExtractor extractor, LinkSelector selector, string fileName, Dictionary<Uri, object> alreadyVisitedUris) {
            _Extractor = extractor;
            _Selector = selector;
            _FileName = fileName;
            _AlreadyListedUris = alreadyVisitedUris != null ? alreadyVisitedUris : new Dictionary<Uri, object>();
        }

        #region UrlProcessedHandler Members
        /// <summary>Handles the <see cref="WebCrawlerEvents.UrlProcessed"/> event fired by the web crawler when registered to do so.</summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="argument">Details of the processed url.</param>
        /// <remarks>This implementation finds all links in a file and saves those links out to a file.</remarks>
        public void UrlProcessed(object sender, Eas.WebCrawler.Interfaces.UrlCompletedEventArgs argument) {
            lock (_Lock) {
                using (System.IO.TextReader reader = argument.Storage.GetTextReader()) {
                    foreach (Uri u in _Extractor.GetLinks(argument.State, reader)) {
                        Request newRequest = new Request(u, argument.State.Request);
                        if (!_AlreadyListedUris.ContainsKey(newRequest.Uri)) {
                            if (_Selector.ShouldFollow(newRequest)) {
                                using (System.IO.StreamWriter writer = new System.IO.StreamWriter(_FileName, true)) {
                                    writer.WriteLine(newRequest.Uri.ToString());
                                }
                                _AlreadyListedUris.Add(newRequest.Uri, null);
                            }
                        }
                    }
                }
            }
        }
        
        #endregion

    }
}
